package springdao.reposotory;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import org.hibernate.FlushMode;
import org.hibernate.Hibernate;
import org.hibernate.Query;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.collection.AbstractPersistentCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.orm.hibernate3.HibernateAccessor;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import springdao.support.AliasHelper;
import springdao.DaoRepository;
import springdao.model.Member;

/**
 *
 * @author Kent Yeh
 */
public class AnnotherDaoRepository extends HibernateDaoSupport implements DaoRepository<Member> {

    static Logger logger = LoggerFactory.getLogger(AnnotherDaoRepository.class);

    public Class<Member> getClazz() {
        return Member.class;
    }

    private LockMode getLockMode(String lockMode) {
        LockMode mode = LockMode.NONE;
        try {
            mode = LockMode.parse(lockMode);
        } catch (RuntimeException ex) {
            mode = LockMode.NONE;
        }
        return mode;
    }

    protected void checkWriteOperationAllowed(Session session) throws InvalidDataAccessApiUsageException {
        if (getHibernateTemplate().isCheckWriteOperations()
                && getHibernateTemplate().getFlushMode() != HibernateAccessor.FLUSH_EAGER
                && session.getFlushMode().lessThan(FlushMode.COMMIT)) {
            throw new InvalidDataAccessApiUsageException(
                    "Write operations are not allowed in read-only mode (FlushMode.MANUAL): "
                    + "Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.");
        }
    }

    protected void prepareQuery(Query queryObject) {
        HibernateTemplate ht = getHibernateTemplate();
        if (ht.isCacheQueries()) {
            queryObject.setCacheable(true);
            if (ht.getQueryCacheRegion() != null) {
                queryObject.setCacheRegion(ht.getQueryCacheRegion());
            }
        }
        if (ht.getFetchSize() > 0) {
            queryObject.setFetchSize(ht.getFetchSize());
        }
        if (ht.getMaxResults() > 0) {
            queryObject.setMaxResults(ht.getMaxResults());
        }
        SessionFactoryUtils.applyTransactionTimeout(queryObject, getSessionFactory());
    }

    @Override
    public boolean isJpql() {
        return false;
    }

    @Override
    public void setJpql(boolean jpql) {
        //
    }

    @Override
    public Member instanate() throws InstantiationException, IllegalAccessException {
        try {
            return getClazz().newInstance();
        } catch (InstantiationException ex) {
            logger.error("Instanate error", ex);
            throw ex;
        } catch (IllegalAccessException ex) {
            logger.error("Instanate error", ex);
            throw ex;
        }
    }

    @Override
    public void clear() {
        getHibernateTemplate().execute(new HibernateCallback<Void>() {

            public Void doInHibernate(Session session) throws HibernateException, SQLException {
                session.clear();
                return null;
            }
        });
    }

    @Override
    public boolean contains(final Object entity) {
        return getHibernateTemplate().execute(new HibernateCallback<Boolean>() {

            public Boolean doInHibernate(Session session) throws HibernateException, SQLException {
                return session.contains(entity);
            }
        });
    }

    @Override
    public Member findByPrimaryKey(Serializable primaryKey) {
        return getHibernateTemplate().load(getClazz(), primaryKey);
    }

    @Override
    public Member findByPrimaryKey(Serializable primaryKey, String lockMode) {
        return getHibernateTemplate().load(getClazz(), primaryKey, getLockMode(lockMode));
    }

    /**
     * 
     * @param primaryKey
     * @param properties Not used.不使用
     * @return 
     */
    @Override
    public Member findByPrimaryKey(Serializable primaryKey, Map<String, Object> properties) {
        return findByPrimaryKey(primaryKey);
    }

    /**
     * 
     * @param primaryKey
     * @param lockMode
     * @param properties Not used.不使用
     * @return 
     */
    @Override
    public Member findByPrimaryKey(Serializable primaryKey, String lockMode, Map<String, Object> properties) {
        return findByPrimaryKey(primaryKey, lockMode);
    }

    @Override
    public Member save(Member entity) {
        getHibernateTemplate().save(entity);
        getHibernateTemplate().flush();
        return entity;
    }

    @Override
    public Collection<Member> save(final Collection<Member> entities) {
        return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<Collection<Member>>() {

            public Collection<Member> doInHibernate(Session session) throws HibernateException {
                checkWriteOperationAllowed(session);
                for (Object entity : entities) {
                    session.save(entity);
                }
                session.flush();
                return entities;
            }
        });
    }

    @Override
    public Member persist(Member entity) {
        getHibernateTemplate().persist(entity);
        getHibernateTemplate().flush();
        return entity;
    }

    @Override
    public Member persist(String entityName, Member entity) {
        getHibernateTemplate().persist(entityName, entity);
        getHibernateTemplate().flush();
        return entity;
    }

    @Override
    public Member update(Member entity) {
        getHibernateTemplate().update(entity);
        getHibernateTemplate().flush();
        return entity;
    }

    @Override
    public Member update(Member entity, String lockMode) {
        getHibernateTemplate().update(entity, getLockMode(lockMode));
        getHibernateTemplate().flush();
        return entity;
    }

    @Override
    public Collection<Member> update(final Collection<Member> entities) {
        return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<Collection<Member>>() {

            public Collection<Member> doInHibernate(Session session) throws HibernateException {
                checkWriteOperationAllowed(session);
                for (Object entity : entities) {
                    session.update(entity);
                }
                session.flush();
                return entities;
            }
        });
    }

    @Override
    public Member merge(Member entity) {
        entity = getHibernateTemplate().merge(entity);
        getHibernateTemplate().flush();
        return entity;
    }

    @Override
    public Member merge(String entityName, Member entity) {
        entity = getHibernateTemplate().merge(entityName, entity);
        getHibernateTemplate().flush();
        return entity;
    }

    public Collection<Member> merge(final Collection<Member> entities) {
        return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<Collection<Member>>() {

            public Collection<Member> doInHibernate(Session session) throws HibernateException {
                checkWriteOperationAllowed(session);
                for (Object entity : entities) {
                    session.merge(entity);
                }
                session.flush();
                return entities;
            }
        });
    }

    @Override
    public Member saveOrUpdate(Member entity) {
        getHibernateTemplate().saveOrUpdate(entity);
        getHibernateTemplate().flush();
        return entity;
    }

    @Override
    public Member saveOrUpdate(String entityName, Member entity) {
        getHibernateTemplate().saveOrUpdate(entityName, entity);
        getHibernateTemplate().flush();
        return entity;
    }

    @Override
    public Collection<Member> saveOrUpdate(Collection<Member> entities) {
        getHibernateTemplate().saveOrUpdateAll(entities);
        return entities;
    }

    @Override
    public void delete(Member entity) {
        getHibernateTemplate().delete(entity);
        getHibernateTemplate().flush();
    }

    @Override
    public void delete(Member entity, String lockMode) {
        getHibernateTemplate().delete(entity, getLockMode(lockMode));
        getHibernateTemplate().flush();
    }

    @Override
    public void delete(final Collection<Member> entities) {
        getHibernateTemplate().executeWithNativeSession(new HibernateCallback<Object>() {

            public Object doInHibernate(Session session) throws HibernateException {
                checkWriteOperationAllowed(session);
                for (Object entity : entities) {
                    session.delete(entity);
                }
                session.flush();
                return null;
            }
        });
    }

    @Override
    public Member lock(Member entity, String lockMode) {
        getHibernateTemplate().lock(entity, getLockMode(lockMode));
        return entity;
    }

    @Override
    public Member lock(String entityName, Member entity, String lockMode) {
        getHibernateTemplate().lock(entityName, entity, getLockMode(lockMode));
        return entity;
    }

    @Override
    public Member refresh(Member entity) {
        getHibernateTemplate().refresh(entity);
        return entity;
    }

    @Override
    public Member refresh(Member entity, String lockMode) {
        getHibernateTemplate().refresh(entity);
        return entity;
    }

    @Override
    public int bulkUpdate(String QL) {
        int res = getHibernateTemplate().bulkUpdate(QL);
        return res;
    }

    @Override
    public List<Integer> bulkUpdate(final List<String> QLs) {

        return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<List<Integer>>() {

            public List<Integer> doInHibernate(Session session) throws HibernateException {
                List<Integer> res = new ArrayList<Integer>();
                for (String queryString : QLs) {
                    Query queryObject = session.createQuery(queryString);
                    prepareQuery(queryObject);
                    res.add(queryObject.executeUpdate());
                }
                return res;
            }
        });
    }

    @Override
    public int bulkUpdate(String QL, Object... parameters) {
        if (parameters == null || parameters.length == 0) {
            return bulkUpdate(QL);
        } else {
            int res = getHibernateTemplate().bulkUpdate(QL, parameters);
            return res;
        }
    }

    @Override
    public int bulkUpdate(final String QL, final Map<String, ?> parameters) {
        if (parameters == null || parameters.isEmpty()) {
            return bulkUpdate(QL);
        } else {
            return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<Integer>() {

                public Integer doInHibernate(Session session) throws HibernateException, SQLException {
                    Query query = session.createQuery(QL);
                    prepareQuery(query);
                    if (parameters != null && !parameters.isEmpty()) {
                        for (String key : parameters.keySet()) {
                            query.setParameter(key, parameters.get(key));
                        }
                    }
                    int res = query.executeUpdate();
                    return res;
                }
            });
        }
    }

    @Override
    public String getEntityName() {
        return getClazz().getName();
    }

    @Override
    public String getAliasName() {
        return AliasHelper.getAlias(getClazz());
    }

    @Override
    public List<Member> findByCriteria(String qlCriteria) {
        return findByCriteria(qlCriteria, 0, 0);
    }

    @Override
    public List<Member> findByCriteria(String qlCriteria, Object... parameters) {
        StringBuilder sb = new StringBuilder("from ").append(getEntityName()).
                append(" as ").append(getAliasName()).append(" ").append(qlCriteria);
        return (List<Member>) getHibernateTemplate().find(sb.toString(), parameters);
    }

    @Override
    public List<Member> findByCriteria(String qlCriteria, final Map<String, ?> parameters) {
        final StringBuilder sb = new StringBuilder("from ").append(getEntityName()).
                append(" as ").append(getAliasName()).append(" ").append(qlCriteria);
        return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<List<Member>>() {

            public List<Member> doInHibernate(Session session) throws HibernateException, SQLException {
                Query query = session.createQuery(sb.toString());
                prepareQuery(query);
                if (parameters != null && !parameters.isEmpty()) {
                    for (String key : parameters.keySet()) {
                        query.setParameter(key, parameters.get(key));
                    }
                }
                return (List<Member>) query.list();
            }
        });
    }

    @Override
    public List<Member> findByJoinCriteria(String joins, String qlCriteria) {
        return findByJoinCriteria(joins, qlCriteria, 0, 0);
    }

    @Override
    public List<Member> findByJoinCriteria(String joins, String qlCriteria, Object... parameters) {
        StringBuilder sb = new StringBuilder("select distinct ").append(getAliasName()).
                append(" from ").append(getEntityName()).append(" as ").append(getAliasName()).append(" ").
                append(joins).append(" ").append(qlCriteria);
        return (List<Member>) getHibernateTemplate().find(sb.toString(), parameters);
    }

    @Override
    public List<Member> findByJoinCriteria(String joins, String qlCriteria, final Map<String, ?> parameters) {
        final StringBuilder sb = new StringBuilder("select distinct ").append(getAliasName()).
                append(" from ").append(getEntityName()).append(" as ").append(getAliasName()).append(" ").
                append(joins).append(" ").append(qlCriteria);
        return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<List<Member>>() {

            public List<Member> doInHibernate(Session session) throws HibernateException, SQLException {
                Query query = session.createQuery(sb.toString());
                prepareQuery(query);
                if (parameters != null && !parameters.isEmpty()) {
                    for (String key : parameters.keySet()) {
                        query.setParameter(key, parameters.get(key));
                    }
                }
                return (List<Member>) query.list();
            }
        });
    }

    @Override
    public List<Member> findByCriteria(final String qlCriteria, final int startPageNo, final int pageSize, final Object... parameters) {
        if ((startPageNo < 1) || (pageSize < 1)) {
            return parameters == null || parameters.length == 0 ? findByCriteria(qlCriteria) : findByCriteria(qlCriteria, parameters);
        } else if (parameters == null || parameters.length == 0) {
            return findByCriteria(qlCriteria, startPageNo, pageSize);
        } else {
            return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<List<Member>>() {

                @Override
                public List<Member> doInHibernate(Session session) throws HibernateException, SQLException {
                    StringBuilder sb = new StringBuilder(" from ").append(getEntityName()).append(" as ").
                            append(getAliasName()).append(" ").append(qlCriteria);
                    Query query = session.createQuery(sb.toString());
                    prepareQuery(query);
                    query.setFirstResult((startPageNo - 1) * pageSize).setMaxResults(pageSize);
                    for (int i = 0; i < parameters.length; i++) {
                        query.setParameter(i, parameters[i]);
                    }
                    return query.list();
                }
            });
        }
    }

    @Override
    public List<Member> findByCriteria(final String qlCriteria, final int startPageNo, final int pageSize, final Map<String, ?> parameters) {
        if ((startPageNo < 1) || (pageSize < 1)) {
            return parameters == null || parameters.isEmpty() ? findByCriteria(qlCriteria) : findByCriteria(qlCriteria, parameters);
        } else if (parameters == null || parameters.isEmpty()) {
            return findByCriteria(qlCriteria, startPageNo, pageSize);
        } else {
            return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<List<Member>>() {

                @Override
                public List<Member> doInHibernate(Session session) throws HibernateException, SQLException {
                    StringBuilder sb = new StringBuilder(" from ").append(getEntityName()).append(" as ").
                            append(getAliasName()).append(" ").append(qlCriteria);
                    Query query = session.createQuery(sb.toString());
                    prepareQuery(query);
                    query.setFirstResult((startPageNo - 1) * pageSize).setMaxResults(pageSize);
                    for (String key : parameters.keySet()) {
                        query.setParameter(key, parameters.get(key));
                    }
                    return query.list();
                }
            });
        }
    }

    @Override
    public List<Member> findByJoinCriteria(final String joins, final String qlCriteria, final int startPageNo, final int pageSize, final Object... parameters) {
        if ((startPageNo < 1) || (pageSize < 1)) {
            return parameters == null || parameters.length == 0 ? findByJoinCriteria(joins, qlCriteria)
                    : findByJoinCriteria(joins, qlCriteria, parameters);
        } else {
            if (parameters == null || parameters.length == 0) {
                return findByCriteria(qlCriteria, startPageNo, pageSize);
            } else {
                return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<List<Member>>() {

                    @Override
                    public List<Member> doInHibernate(Session session) throws HibernateException, SQLException {
                        StringBuilder sb = new StringBuilder("select ").append(getAliasName()).
                                append(" from ").append(getEntityName()).append(" as ").append(getAliasName()).
                                append(" ").append(joins).append(" ").append(qlCriteria);
                        Query query = session.createQuery(sb.toString());
                        prepareQuery(query);
                        query.setFirstResult((startPageNo - 1) * pageSize).setMaxResults(pageSize);
                        for (int i = 0; i < parameters.length; i++) {
                            query.setParameter(i, parameters[i]);
                        }
                        return query.list();
                    }
                });
            }
        }
    }

    @Override
    public List<Member> findByJoinCriteria(final String joins, final String qlCriteria, final int startPageNo, final int pageSize, final Map<String, ?> parameters) {
        if ((startPageNo < 1) || (pageSize < 1)) {
            return parameters == null || parameters.isEmpty() ? findByJoinCriteria(joins, qlCriteria)
                    : findByJoinCriteria(joins, qlCriteria, parameters);
        } else {
            if (parameters == null || parameters.isEmpty()) {
                return findByCriteria(qlCriteria, startPageNo, pageSize);
            } else {
                return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<List<Member>>() {

                    @Override
                    public List<Member> doInHibernate(Session session) throws HibernateException, SQLException {
                        StringBuilder sb = new StringBuilder("select ").append(getAliasName()).
                                append(" from ").append(getEntityName()).append(" as ").append(getAliasName()).
                                append(" ").append(joins).append(" ").append(qlCriteria);
                        Query queryObject = session.createQuery(sb.toString());
                        prepareQuery(queryObject);
                        queryObject.setFirstResult((startPageNo - 1) * pageSize).setMaxResults(pageSize);
                        for (String key : parameters.keySet()) {
                            queryObject.setParameter(key, parameters.get(key));
                        }
                        return queryObject.list();
                    }
                });
            }
        }
    }

    @Override
    public List<Member> findByCriteria(final String qlCriteria, final int startPageNo, final int pageSize) {
        if ((startPageNo < 1) || (pageSize < 1)) {
            StringBuilder sb = new StringBuilder("from ").append(getEntityName()).
                    append(" as ").append(getAliasName()).append(" ").append(qlCriteria);
            return getHibernateTemplate().find(sb.toString());
        } else {
            return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<List<Member>>() {

                @Override
                public List<Member> doInHibernate(Session session) throws HibernateException, SQLException {
                    StringBuffer sb = new StringBuffer("from ").append(getEntityName()).append(" as ").
                            append(getAliasName()).append(" ").append(qlCriteria);
                    Query queryObject = session.createQuery(sb.toString());
                    prepareQuery(queryObject);
                    queryObject.setFirstResult((startPageNo - 1) * pageSize).setMaxResults(pageSize);
                    return queryObject.list();
                }
            });
        }
    }

    @Override
    public List<Member> findByJoinCriteria(String joins, String qlCriteria, final int startPageNo, final int pageSize) {
        final StringBuilder sb = new StringBuilder("select distinct ").append(getAliasName()).
                append(" from ").append(getEntityName()).append(" as ").append(getAliasName()).
                append(" ").append(joins).append(" ").append(qlCriteria);
        if ((startPageNo < 1) || (pageSize < 1)) {
            return getHibernateTemplate().find(sb.toString());
        } else {
            return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<List<Member>>() {

                @Override
                public List<Member> doInHibernate(Session session) throws HibernateException, SQLException {
                    Query queryObject = session.createQuery(sb.toString());
                    prepareQuery(queryObject);
                    queryObject.setFirstResult((startPageNo - 1) * pageSize).setMaxResults(pageSize);
                    return queryObject.list();
                }
            });
        }
    }

    /**
     * Return entities by execute Query.<br/>
     * Whether result is cacheable by your envoirment.<br/>
     * 執行Query以取得List&lt;E&gt;<br/>
     * 回傳結果是否Cacheable由整體環境設定決定
     * @param query
     * @return
     */
    protected List<Member> executeList(Query query) {
        return executeList(query, new Object[]{});
    }

    protected List<Member> executeList(final Query query, final Object... values) {
        return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<List<Member>>() {

            @Override
            public List<Member> doInHibernate(Session session) throws HibernateException, SQLException {
                prepareQuery(query);
                if (values != null && values.length > 0) {
                    for (int i = 0; i < values.length; i++) {
                        query.setParameter(i, values[i]);
                    }
                }
                return query.list();
            }
        });
    }

    /**
     * Return a new {@link SQLQuery} by a sql statement.<br/>
     * 以SQL Statement 取得{@link SQLQuery 查詢}物件，執行時最好透過{@link #executeList(org.hibernate.Query)  executeList(Query)}或是
     * {@link #executeList(org.hibernate.Query) executeList(Query)執行以取得想要的結果}
     * @param sql
     * @return
     */
    protected SQLQuery getSQLQuery(String sql) {
        return getSQLQuery(sql, true);
    }

    protected SQLQuery getSQLQuery(String sql, boolean readOnly) {
        SQLQuery qry = getSession().createSQLQuery(sql);
        qry.setReadOnly(readOnly);
        qry.setCacheable(false);
        return qry;
    }

    @Override
    public List<Member> findBySQLQuery(String sql) {
        return executeList(getSQLQuery(sql).addEntity(getClazz()));
    }

    @Override
    public List<Member> findBySQLQuery(String sql, Object... parameters) {
        return executeList(getSQLQuery(sql).addEntity(getClazz()), parameters);
    }

    @Override
    public List<Member> findBySQLQuery(String sql, Map<String, ?> parameters) {
        return executeList(getSQLQuery(sql).addEntity(getClazz()), parameters);
    }

    @Override
    public List<Member> findBySQLQuery(String sql, String entityAlias) {
        return executeList(getSQLQuery(sql).addEntity(entityAlias, getClazz()));
    }

    @Override
    public List<Member> findBySQLQuery(String sql, String entityAlias, Object... parameters) {
        return executeList(getSQLQuery(sql).addEntity(entityAlias, getClazz()), parameters);
    }

    @Override
    public List<Member> findBySQLQuery(String sql, String entityAlias, Map<String, ?> parameters) {
        return executeList(getSQLQuery(sql).addEntity(entityAlias, getClazz()), parameters);
    }

    @Override
    public <T> T findUniqueByQL(Class<T> clazz, String QL) {
        return findUniqueByQL(clazz, QL, (Object[]) null);
    }

    @Override
    public <T> T findUniqueByQL(Class<T> clazz, final String QL, final Object... parameters) {
        return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<T>() {

            @Override
            public T doInHibernate(Session session) throws HibernateException, SQLException {
                Query query = session.createQuery(QL).setCacheable(false).setReadOnly(true);
                if (parameters != null && parameters.length > 0) {
                    for (int i = 0; i < parameters.length; i++) {
                        query.setParameter(i, parameters[i]);
                    }
                }
                return (T) query.uniqueResult();
            }
        });
    }

    @Override
    public <T> T findUniqueByQL(Class<T> clazz, final String QL, final Map<String, ?> parameters) {
        return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<T>() {

            @Override
            public T doInHibernate(Session session) throws HibernateException, SQLException {
                Query query = session.createQuery(QL).setCacheable(false).setReadOnly(true);
                if (parameters != null && !parameters.isEmpty()) {
                    for (String key : parameters.keySet()) {
                        query.setParameter(key, parameters.get(key));
                    }
                }
                return (T) query.uniqueResult();
            }
        });
    }

    @Override
    public <T> List<T> findListByQL(Class<T> clazz, String QL) {
        return findListByQL(clazz, QL, (Object[]) null);
    }

    @Override
    public <T> List<T> findListByQL(Class<T> clazz, final String QL, final Object... parameters) {
        return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<List<T>>() {

            @Override
            public List<T> doInHibernate(Session session) throws HibernateException, SQLException {
                Query query = session.createQuery(QL).setCacheable(false).setReadOnly(true);
                if (parameters != null && parameters.length > 0) {
                    for (int i = 0; i < parameters.length; i++) {
                        query.setParameter(i, parameters[i]);
                    }
                }
                return query.list();
            }
        });
    }

    @Override
    public <T> List<T> findListByQL(Class<T> clazz, final String QL, final Map<String, ?> parameters) {
        return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<List<T>>() {

            @Override
            public List<T> doInHibernate(Session session) throws HibernateException, SQLException {
                Query query = session.createQuery(QL).setCacheable(false).setReadOnly(true);
                if (parameters != null && !parameters.isEmpty()) {
                    for (String key : parameters.keySet()) {
                        query.setParameter(key, parameters.get(key));
                    }
                }
                return query.list();
            }
        });
    }

    public List<Member> findByNamedQuery(final String name) {
        return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<List<Member>>() {

            public List<Member> doInHibernate(Session session) throws HibernateException, SQLException {
                return session.getNamedQuery(name).list();
            }
        });
    }

    public List<Member> findByNamedQuery(final String name, final Object... parameters) {
        return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<List<Member>>() {

            public List<Member> doInHibernate(Session session) throws HibernateException, SQLException {
                Query query = session.getNamedQuery(name);
                if (parameters != null && parameters.length > 0) {
                    for (int i = 0; i < parameters.length; i++) {
                        query.setParameter(i, parameters[i]);
                    }
                }
                return query.list();
            }
        });
    }

    public List<Member> findByNamedQuery(final String name, final Map<String, ?> parameters) {
        return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<List<Member>>() {

            public List<Member> doInHibernate(Session session) throws HibernateException, SQLException {
                Query query = session.getNamedQuery(name);
                if (parameters != null && !parameters.isEmpty()) {
                    for (String key : parameters.keySet()) {
                        query.setParameter(key, parameters.get(key));
                    }
                }
                return query.list();
            }
        });
    }

    @Override
    public <T> List<T> findListByNamedQuery(Class<T> clazz, final String name) {
        return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<List<T>>() {

            public List<T> doInHibernate(Session session) throws HibernateException, SQLException {
                return session.getNamedQuery(name).list();
            }
        });
    }

    @Override
    public <T> List<T> findListByNamedQuery(Class<T> clazz, final String name, final Object... parameters) {
        return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<List<T>>() {

            public List<T> doInHibernate(Session session) throws HibernateException, SQLException {
                Query query = session.getNamedQuery(name);
                if (parameters != null && parameters.length > 0) {
                    for (int i = 0; i < parameters.length; i++) {
                        query.setParameter(i, parameters[i]);
                    }
                }
                return query.list();
            }
        });
    }

    @Override
    public <T> List<T> findListByNamedQuery(Class<T> clazz, final String name, final Map<String, ?> parameters) {
        return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<List<T>>() {

            public List<T> doInHibernate(Session session) throws HibernateException, SQLException {
                Query query = session.getNamedQuery(name);
                if (parameters != null && !parameters.isEmpty()) {
                    for (String key : parameters.keySet()) {
                        query.setParameter(key, parameters.get(key));
                    }
                }
                return query.list();
            }
        });
    }

    public Member initLazyCollection(final Member entity, final String collectionFieldName) {
        final AtomicBoolean found = new AtomicBoolean(false);
        final String methodName = collectionFieldName.matches("^[a-z][A-Z]") ? collectionFieldName : collectionFieldName.length() > 1
                ? collectionFieldName.substring(0, 1).toUpperCase() + collectionFieldName.substring(1) : collectionFieldName.toUpperCase();
        return getHibernateTemplate().executeWithNativeSession(new HibernateCallback<Member>() {

            public Member doInHibernate(final Session session) throws HibernateException, SQLException {

                ReflectionUtils.doWithMethods(getClazz(),
                        new ReflectionUtils.MethodCallback() {

                            public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
                                try {
                                    Method setter = entity.getClass().getMethod("s" + method.getName().substring(1), method.getReturnType());
                                    Object fieldObj = method.invoke(entity, new Object[]{});
                                    if (fieldObj instanceof AbstractPersistentCollection) {
                                        AbstractPersistentCollection ps = (AbstractPersistentCollection) fieldObj;
                                        if (!ps.wasInitialized() && ps.getSession() == null) {
                                            session.update(entity);
                                            Hibernate.initialize(ps);
                                            setter.invoke(entity, ps);
                                        }
                                    }
                                } catch (NoSuchMethodException ex) {
                                    throw new HibernateException("Setter " + getClazz().getSimpleName() + ".set" + methodName + "(...) not found.", ex);
                                } catch (InvocationTargetException ex) {
                                    throw new HibernateException("Could not fetch Collection from " + getClazz().getSimpleName() + "." + method.getName(), ex);
                                }
                            }
                        },
                        new ReflectionUtils.MethodFilter() {

                            public boolean matches(Method method) {
                                if (found.get()) {
                                    return false;
                                } else {
                                    found.set(method.getName().equals("get" + methodName) && method.getParameterTypes().length == 0
                                            && ClassUtils.isAssignable(Collection.class, method.getReturnType()));
                                    return found.get();
                                }
                            }
                        });
                if (!found.get()) {
                    throw new HibernateException("Getter " + getClazz().getSimpleName() + ".get" + methodName + "() not found.");
                }
                return entity;
            }
        });
    }
}
